<template>
  <Transition
    :enter-active-class="`animate__animated ${enterAnimation}`"
    :leave-active-class="`animate__animated ${leaveAnimation}`"
    mode="out-in"
  >
    <div
      ref="boxRef"
      class="moveable-box"
      :class="{ locked: props.locked }"
      :style="{
        position: 'absolute',
        left: x + 'px',
        top: y + 'px',
        width: width + 'px',
        height: height + 'px',
        transform: `rotate(${rotate}deg)`,
        zIndex: zIndex || 1,
        ...boxStyle
      }"
      v-show="props.visible ?? true"
    >
      <slot />
    </div>
  </Transition>
</template>

<script setup lang="ts">
import { ref, onMounted, onBeforeUnmount, watch, shallowRef, computed } from 'vue'
import Moveable from 'moveable'
import 'animate.css';
import type {
  OnDrag, 
  OnResize, 
  OnDragStart, 
  OnDragEnd, 
  OnResizeStart, 
  OnResizeEnd 
} from 'moveable'
import { throttle } from 'lodash-es'

const props = withDefaults(defineProps<{
  id?: string
  x?: number
  y?: number
  width?: number
  height?: number
  rotate?: number
  canvasScale?: number
  canvasOffset?: { x: number; y: number }
  zIndex?: number
  boxStyle?: Record<string, any>
  active?: boolean
  hideHandles?: boolean
  locked?: boolean
  shank?: boolean
  visible?: boolean
  canvasWidth?: number
  canvasHeight?: number
  // 新增动画相关props
  enterAnimation?: string
  leaveAnimation?: string
  animationDuration?: number
}>(), {
  x: 0,
  y: 0,
  width: 0,
  height: 0,
  rotate: 0,
  canvasScale: 1,
  canvasOffset: () => ({ x: 0, y: 0 }),
  zIndex: 1,
  boxStyle: () => ({}),
  active: false,
  hideHandles: false,
  locked: false,
  visible: true,
  shank: false,
  // 默认动画效果
  enterAnimation: '',
  leaveAnimation: '',
  animationDuration: 0.5
})

const emit = defineEmits(['update', 'dragEnd', 'dragStart'])

const boxRef = shallowRef<HTMLElement | null>(null)
let moveable: Moveable | null = null
let rafId: number

// 计算样式，包含动画持续时间
const computedBoxStyle = computed(() => {
  const style = {
    position: 'absolute',
    left: props.x + 'px',
    top: props.y + 'px',
    width: props.width + 'px',
    height: props.height + 'px',
    transform: `rotate(${props.rotate}deg)`,
    zIndex: props.zIndex || 1,
    ...props.boxStyle
  }
  
  // 如果设置了动画持续时间，添加到样式中
  if (props.animationDuration) {
    style['--animate-duration'] = props.animationDuration+"s"
  }
  
  return style
})

// 边界检查
const checkBoundary = (left: number, top: number) => {
  const minX = 0, minY = 0
  const maxX = (props.canvasWidth || window.innerWidth) - props.width
  const maxY = (props.canvasHeight || window.innerHeight) - props.height
  return {
    x: Math.max(minX, Math.min(left, maxX)),
    y: Math.max(minY, Math.min(top, maxY))
  }
}

// 节流更新
const emitUpdate = throttle((data: any) => {
  cancelAnimationFrame(rafId)
  rafId = requestAnimationFrame(() => {
    emit('update', data)
  })
}, 16) // 约60fps

// 设置Moveable事件
const setupMoveableEvents = () => {
  if (!moveable || props.locked) return

  const handlers = {
    drag: (drag: { left: number; top: number }) => {
      const { x, y } = checkBoundary(drag.left, drag.top)
      emitUpdate({
        id: props.id!,
        x,
        y,
        width: props.width!,
        height: props.height!,
        rotate: props.rotate || 0
      })
    },
    resize: ({ width, height, drag }: { width: number; height: number; drag: any }) => {
      const { x, y } = checkBoundary(drag.left, drag.top)
      emitUpdate({
        id: props.id!,
        x,
        y,
        width,
        height,
        rotate: props.rotate || 0
      })
    },
    dragStart: () => {
      emit('dragStart', {
        id: props.id!,
        x: props.x,
        y: props.y,
        width: props.width,
        height: props.height,
        rotate: props.rotate || 0
      })
    },
    dragEnd: () => {
      emit('dragEnd', {
        id: props.id!,
        x: props.x,
        y: props.y,
        width: props.width,
        height: props.height,
        rotate: props.rotate || 0
      })
    },
    resizeStart: () => {
      emit('dragStart', {
        id: props.id!,
        x: props.x,
        y: props.y,
        width: props.width,
        height: props.height,
        rotate: props.rotate || 0
      })
    },
    resizeEnd: () => {
      emit('dragEnd', {
        id: props.id!,
        x: props.x,
        y: props.y,
        width: props.width,
        height: props.height,
        rotate: props.rotate || 0
      })
    }
  }

  Object.entries(handlers).forEach(([event, handler]) => {
    moveable!.on(event as any, handler)
  })
}

// 切换手柄显示
const toggleHandles = () => {
  if (!moveable) return

  if (props.shank === false) {
    moveable.renderDirections = []
    moveable.rotationPosition = 'none'
    return
  }

  if (props.active && !props.hideHandles) {
    moveable.renderDirections = ['n', 'ne', 'e', 'se', 's', 'sw', 'w', 'nw']
    moveable.rotationPosition = 'top'
  } else {
    moveable.renderDirections = []
    moveable.rotationPosition = 'none'
  }
}

// 更新Moveable实例
const updateMoveable = () => {
  if (moveable && boxRef.value) {
    moveable.updateRect()
  }
}

// 创建Moveable实例
const createMoveable = () => {
  if (moveable || !boxRef.value) return
  moveable = new Moveable(document.body, {
    target: boxRef.value,
    draggable: !props.locked,
    resizable: true,
    rotatable: false,
    zoom: 1,
    origin: false,
    keepRatio: false,
    snappable: false,
    useMutationObserver: false,
    elementGuidelines: [],
    throttleResize: 1,
    edge: false,
    bounds: { left: 0, top: 0, right: props.canvasWidth|| window.innerWidth, bottom: props.canvasHeight|| window.innerHeight }
  })

  setupMoveableEvents()
  toggleHandles()
  updateMoveable()
}

// 响应式监听
watch(() => props.active, (val) => {
  if (moveable) {
    toggleHandles()
    updateMoveable()
  }
}, { immediate: true })

watch(() => props.hideHandles, () => {
  if (moveable) {
    toggleHandles()
    updateMoveable()
  }
}, { immediate: true })

watch(() => props.locked, (val) => {
  if (moveable) {
    moveable.draggable = !val
    setupMoveableEvents()
    updateMoveable()
  }
})

watch(() => props.shank, () => {
  if (moveable) {
    toggleHandles()
    updateMoveable()
  }
})

watch(() => props.canvasHeight, () => {
  if (moveable) {
    toggleHandles()
    updateMoveable()
  }
})
watch(() => props.canvasWidth, () => {
  if (moveable) {
    toggleHandles()
    updateMoveable()
  }
})

// 生命周期
onMounted(() => {
  createMoveable()
})

onBeforeUnmount(() => {
  if (moveable) {
    moveable.off('drag')
    moveable.off('resize')
    moveable.off('dragStart')
    moveable.off('dragEnd')
    moveable.off('resizeStart')
    moveable.off('resizeEnd')
    moveable.destroy()
    moveable = null
  }
  cancelAnimationFrame(rafId)
})
</script>

<style scoped>
.moveable-box {
  box-sizing: border-box;
  padding: 0;
  margin: 0;
  border: none;
  pointer-events: auto;
  transition: transform 0.1s ease-out;
  will-change: transform;
  backface-visibility: hidden;
}

.moveable-box.locked {
  /* cursor: not-allowed !important; */
  pointer-events: auto;
  /* opacity: 0.7; */
}
</style>